
/* Author: Ratish J. Punnoose, 2006
 * This file is part of TiROS, the Tickless Real-Time Operating System.
 * Copyright(c) 2006, 2007: Ratish J. Punnoose.
 * Copyright(c) 2006 Sandia Corporation. Under the terms of Contract
 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
 * certain rights in this software. 
 * 
 * TiROS is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 or (at your option) any later version.
 *
 * TiROS is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with TiROS; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * As a special exception, if other files instantiate templates or use macros
 * or inline functions from this file, or you compile this file and link it
 * with other works to produce a work based on this file, this file does not
 * by itself cause the resulting work to be covered by the GNU General Public
 * License. However the source code for this file must still be made available
 * in accordance with section (3) of the GNU General Public License.
 *
 * This exception does not invalidate any other reasons why a work based on
 * this file might be covered by the GNU General Public License.
 *
 */


 /** \file
 * @brief Posix port of the RTOS.  
 *
 * @page posix_port  Posix HAL
 * \section posix_port_section Posix HAL
 * This port makes a posix system look like real
 * hardware to TiROS. TiROS runs within a posix process. 
 * This port provides the 
 * context switching and time 
 * functions to TiROS.
 * It simulates interrupts using posix signals.
 *  The posix port does not use kernel traps (They work
 *  on  Linux but do not work reliably on Cygwin).  
 * The signal implementation under Cygwin is such that storing and
 * restoring a  context within a signal handler does not work
 * properly. So we bypass this for the Cygwin environment and use
 * direct Win32 api calls.
 * This port has been tested on Linux 2.6.x kernel series/Glibc and on
 * Cygwin 5.1.
 * This port does not work on Mac OS X (as of 10.4), because the
 * needed posix functions are not implemented.
 *
 * This port does not allow the time to be set.
 *
 * Currently, this port does not use the user provided stack memory
 * for the task stack.  This port stores the context on the user
 * provided memory.  For the task stack, it allocates new memory
 * (memory size defined by TIROS_POSIX_STKSZ)
 *
 * <H2> Configuration Options: </H2>
 * <DL>
 *  <DT> PORT_TIMER_TYPE : <DD>Options are 1 or 2.  Setting PORT_TIMER_TYPE to 1
 * uses the system real time.  Setting PORT_TIMER_TYPE to 2, uses the
 * process time. This option is irrelevant to Cygwin as only real time
 * is available.
 *  <DT> TIROS_POSIX_STKSZ : <DD> The stack size allocated to each task.
 *  When os_task_create is called, the stack location and stack size
 * for the task are passed along to the port. The Posix port stores
 * the context at this provided stack location.  The context size is
 * fixed based on the registers etc.  The rest of this provided stack
 * is unused.  The stack for the task is allocated separately using a
 * mmap. By default, this stack is 16384 bytes.  This was done because
 * sometimes simple calls like printf  on a posix system will easily
 * overrun a small stack (tens of bytes) that might be specified for an embedded
 * system.  Allocating a larger stack on a posix system might help in
 * running the code on both platforms without modification.  Override
 * the default by specifying TIROS_POSIX_STKSZ.
 * </DL>
 * 
 *  
 * <H2>Writing ISRs for the Posix port</H2>
 *  The posix port uses unix/posix
 * signals to fake interrupts.  Interrupt handlers are thus signal
 * handlers.  The posix port does not use kernel traps (for
 * compatibility).  So the form of the ISR must be as follows.
 * 
 * \code
 * extern int halint_kernel_trap_flagged;  // Flag in hal
 * extern uint8_t os_isr_nesting;
 * void signal_handler(int sig)
 * { 
 *   OS_ISR_BEGIN();
 *   halint_kernel_trap_flagged = 0;
 *   // Perform actions
 *   xxx();
 *   
 *     // Re-enable interrupts if desired
 *   if (os_isr_nesting  < TIROS_MAX_ISR_NESTING) {
 *      OS_INT_ENABLE();
 *      yyy();
 *   } 
 *   zzz();
 *   // End actions
 *   OS_ISR_END();
 *   if (halint_kernel_trap_flagged) {
 *      hal_ctxt_switch();
 *   }
 * }
 * \endcode
 *
 */



#ifndef __POSIX_TIROS_PORT_H
#define __POSIX_TIROS_PORT_H


/*@ignore@*/
#define TIROS_USER_CONFIG
#  include "proj_config.h"
#undef  TIROS_USER_CONFIG
/*@end@*/


#include <signal.h>
#include <stdint.h>

#define subtime_t long
#define LT_INLINE static inline


#include <tiros/tr_time.h>
#include "porttime.h"


/* Define the value of infinite time for this port */
#define TRPORT_INF_TIME {~0 , ~0 }


/* The posix port does not use register passing. It is almost
    impossible to implement in a portable manner. */
/* #define TIROS_REGISTER_PASSING */




#ifndef  TIROS_POSIX_STKSZ  
#define TIROS_POSIX_STKSZ  16384
#endif

/* Define basic types for   the hardware word, boolean, and pointer */
typedef uintptr_t osword_t;
typedef void*        osptr_t;
typedef osword_t*        osstkptr_t;
typedef void (* osfnptr_t)(void *fnarg);
typedef uintptr_t osptrword_t;

#define ILLEGAL_STACK ((osstkptr_t) ~0)
#define TR_ILLEGAL_ADDR  ((osptr_t)    ~0)


/* Port specific function implemented in tr_port.c */

/* \name Context_switching_functions
 * @{ */

#ifdef __CYGWIN__

#include <windows.h>
typedef CONTEXT ctxt_t;

/* The context includes the registers, a place for storing the signal
 * state, and in this case, a location where the pointer to the true
 * posix stack can be stored */
#define TRPORT_MIN_CTXT_SZ    ( (sizeof(ctxt_t) + sizeof(sigset_t) + sizeof(osstkptr_t) )/sizeof(osword_t) )

#else
#include <ucontext.h>
typedef ucontext_t ctxt_t;
#define TRPORT_MIN_CTXT_SZ   ( (sizeof(ctxt_t) + sizeof(osstkptr_t))/sizeof(osword_t) )


#endif

/* \brief Initialize the stack for a task.
 * @param stk  Pointer to the stack.
 * @param stacksize  Size of the stack.
 * @param PC   Program counter.
 * @param init_arg   Initial argument passed to the task 
 * @return  The pointer to the task context to be used in load/save context. */
osstkptr_t hal_stk_init(osword_t *stk, osword_t stacksize, 
		     osfnptr_t pc,
		     osptr_t init_arg); 




/* Enable critical section calls in the the called function
 *
 *  This has to be called at the beginning of the function before
 *  calling OS_CRITICAL_BEGIN or OS_CRITICAL_END.  This should be the
 *  first call after variable declaration.  These section should not
 *  be added within an ISR if Interrupt nesting is turned on, because
 *  the interrupt nesting adds this in automatically, and duplication
 *  will result in compiler complaints. */
#define OS_PORT_CRITICAL_ENABLE()   \
	sigset_t signal_status_old; \
	sigset_t signal_status_new;

#define OS_PORT_ISR_BEGIN()                     		\
	extern uint8_t os_isr_nesting;          		\
	os_isr_nesting++;




/* Begin a critical section 
 * 
 * The current interrupt state is saved in signal_status and then
 * interrupts are disabled */
#define OS_PORT_CRITICAL_BEGIN() \
	sigfillset(&signal_status_new); \
	sigprocmask(SIG_SETMASK, &signal_status_new, &signal_status_old);

/*
#define OS_PORT_CRITICAL_BEGIN() \
	sigemptyset(&signal_status_new); \
        sigaddset(&signal_status_new, SIGALRM);  \
        sigaddset(&signal_status_new, SIGVTALRM);  \
	sigprocmask(SIG_SETMASK, &signal_status_new, &signal_status_old);
*/

/* End a critical section
 *
 * The saved interrupt state is restored */
#define OS_PORT_CRITICAL_END()  \
	sigprocmask(SIG_SETMASK, &signal_status_old, 0);


static inline void OS_PORT_INT_ENABLE(void)
{
	sigset_t signal_status;
	sigemptyset(&signal_status);
	sigprocmask(SIG_SETMASK, &signal_status, 0);
}


static inline void OS_PORT_INT_DISABLE(void)
{
	sigset_t signal_status;
	sigfillset(&signal_status);
	sigprocmask(SIG_SETMASK, &signal_status, 0);
}



/* Context switch from user level (non-ISR)
 *  This function is called from within an OS call, to switch the
 *  running process.  It also provides a return value. This is a
 *  pseudo return-value.  This function does not actually determine
 *  the value returned.  The return value is set by hal_retval_set
 *  which is often invoked when waking the process up from a blocked
 *  state. 
 *  The function takes the following form
 * \code
 * osstkptr_t hal_ctxt_switch(void) 
 * { 
 *   osstkptr_t ctxtptr = storecurrcontext_on_stk();
 *   ctxtptr = osint_taskswitcher(ctxtptr);
 *   loadnewcontext_from_stk(ctxtptr);
 * }
 * \endcode 
 * @return  Return value.  */
osstkptr_t hal_ctxt_switch(void);


void hal_ctxt_load(osstkptr_t ctxt_ptr);


/* @} End context switching functions  */	





void hal_time_get(trtime_t *t);
int hal_time_set(const trtime_t *t);


/* \brief Set an alarm.
 *  When the alarm is reached, osint_alarm_reached is called
 * @param lt Pointer to time */
void  hal_alarm_set(const trtime_t *lt);

void hal_init(void);






extern int halint_kernel_trap_flagged;




static inline void OS_KERNEL_TRAP(void)
{ 	halint_kernel_trap_flagged = 1;  }





/* 10 us response time */
#define TRPORT_RESPONSE_TIME	{0, 10000}


/* Port options are 0, 1.
 * 1 - uses the system real time.
 * 2 - uses the process time. */
#ifndef PORT_TIMER_TYPE
#define PORT_TIMER_TYPE  1
#endif



#ifdef TIROSINT_DATA_DEBUG
/* Compute the program counter, from the stored context (stack
 * pointer)
 * This function is used for debugging.  Implement this by extracting
 * the program counter out of the stack pointer if possible.
 *
 * @param stkptr  The stkptr that is used by the hal to save/load
 * context.
 * @return The program counter */
osptr_t hal_pc_from_ctxt(osstkptr_t stkptr);

/* Return the maximum stack occupancy of the task.
 * This hal implemented function should provide the 
 * maximum stack occupancy of the task whose context is given.
 * This is used for debugging.  It is valid for this function to
 * return 0, if this information is not readily extractable.
 * @param ctxt_ptr        Context of the task.
 * @param base_stkptr   The initialization stack value for this task
 *                       that was passed to hal_stk_init
 * @param stksize        The initial stack size for this task that was 
 *                       passed to hal_stk_init
 * @return The maximum stack occupancy */
osword_t hal_stkusage_max(osstkptr_t ctxt_ptr, osstkptr_t base_stkptr,
			  osword_t stksize);

/* Return the current stack occupancy of the task.
 * This hal implemented function should provide the 
 * current stack occupancy of the task whose context is given.
 * This is used for debugging.  It is valid for this function to
 * return 0, if this information is not readily extractable.
 * @param ctxt_ptr   Context of the task.
 * @param base_stkptr   The initialization stack value for this task
 *                       that was passed to hal_stk_init
 * @param stksize        The initial stack size for this task that was 
 *                       passed to hal_stk_init
 * @return The current stack occupancy */
osword_t hal_stkusage_curr(osstkptr_t ctxt_ptr, osstkptr_t base_stkptr,
			   osword_t stksize);


#endif



#endif   /* End of tr_port.h */
